/******************************************************************************* * Copyright (c) 2000, 2015 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation * Remy Chi Jian Suen <remy.suen@gmail.com> - Bug 43573 [Contributions] Support icon in <menu> *******************************************************************************/ package org.eclipse.ui.internal; import java.util.ArrayList; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.Platform; import org.eclipse.jface.action.AbstractGroupMarker; import org.eclipse.jface.action.GroupMarker; import org.eclipse.jface.action.IContributionItem; import org.eclipse.jface.action.IContributionManager; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.IToolBarManager; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.action.Separator; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.ui.IWorkbenchActionConstants; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.internal.registry.IWorkbenchRegistryConstants; import org.eclipse.ui.internal.registry.RegistryReader; import org.eclipse.ui.plugin.AbstractUIPlugin; /** * This class contains shared functionality for reading action contributions * from plugins into workbench parts (both editors and views). */ public abstract class PluginActionBuilder extends RegistryReader { protected String targetID; protected String targetContributionTag; protected BasicContribution currentContribution; protected ArrayList cache; /** * The default constructor. */ public PluginActionBuilder() { } /** * Contributes submenus and/or actions into the provided menu and tool bar * managers. * * @param menu the menu to contribute to * @param toolbar the toolbar to contribute to * @param appendIfMissing append containers if missing */ public final void contribute(IMenuManager menu, IToolBarManager toolbar, boolean appendIfMissing) { if (cache == null) { return; } for (int i = 0; i < cache.size(); i++) { BasicContribution contribution = (BasicContribution) cache.get(i); contribution.contribute(menu, appendIfMissing, toolbar, appendIfMissing); } } /** * This factory method returns a new ActionDescriptor for the * configuration element. It should be implemented by subclasses. */ protected abstract ActionDescriptor createActionDescriptor( IConfigurationElement element); /** * Factory method to create the helper contribution class that will hold * onto the menus and actions contributed. */ protected BasicContribution createContribution() { return new BasicContribution(); } /** * Returns the name of the part ID attribute that is expected * in the target extension. */ protected String getTargetID(IConfigurationElement element) { String value = element.getAttribute(IWorkbenchRegistryConstants.ATT_TARGET_ID); return value != null ? value : "???"; //$NON-NLS-1$ } /** * Returns the id of this contributions. */ protected String getID(IConfigurationElement element) { String value = element.getAttribute(IWorkbenchRegistryConstants.ATT_ID); return value != null ? value : "???"; //$NON-NLS-1$ } /** * Reads the contributions from the registry for the provided workbench * part and the provided extension point ID. */ protected void readContributions(String id, String tag, String extensionPoint) { cache = null; currentContribution = null; targetID = id; targetContributionTag = tag; readRegistry(Platform.getExtensionRegistry(), PlatformUI.PLUGIN_ID, extensionPoint); } /** * Implements abstract method to handle the provided XML element * in the registry. */ @Override protected boolean readElement(IConfigurationElement element) { String tag = element.getName(); // Ignore all object contributions element as these // are handled by the ObjectActionContributorReader. if (tag.equals(IWorkbenchRegistryConstants.TAG_OBJECT_CONTRIBUTION)) { return true; } // Found top level contribution element if (tag.equals(targetContributionTag)) { if (targetID != null) { // Ignore contributions not matching target id String id = getTargetID(element); if (id == null || !id.equals(targetID)) { return true; } } // Read its sub-elements currentContribution = createContribution(); readElementChildren(element); if (cache == null) { cache = new ArrayList(4); } cache.add(currentContribution); currentContribution = null; return true; } // Found menu contribution sub-element if (tag.equals(IWorkbenchRegistryConstants.TAG_MENU)) { currentContribution.addMenu(element); return true; } // Found action contribution sub-element if (tag.equals(IWorkbenchRegistryConstants.TAG_ACTION)) { currentContribution.addAction(createActionDescriptor(element)); return true; } return false; } /** * Helper class to collect the menus and actions defined within a * contribution element. */ protected static class BasicContribution { protected ArrayList menus; protected ArrayList actions; /** * Add a menu. * * @param element the element to base the menu on */ public void addMenu(IConfigurationElement element) { if (menus == null) { menus = new ArrayList(1); } menus.add(element); } /** * Add an action. * * @param desc the descriptor */ public void addAction(ActionDescriptor desc) { if (actions == null) { actions = new ArrayList(3); } actions.add(desc); } /** * Contributes submenus and/or actions into the provided menu and tool bar * managers. * * The elements added are filtered based on activity enablement. * @param menu the menu to contribute to * @param menuAppendIfMissing whether to append missing groups to menus * @param toolbar the toolbar to contribute to * @param toolAppendIfMissing whether to append missing groups to toolbars */ public void contribute(IMenuManager menu, boolean menuAppendIfMissing, IToolBarManager toolbar, boolean toolAppendIfMissing) { if (menus != null && menu != null) { for (int i = 0; i < menus.size(); i++) { IConfigurationElement menuElement = (IConfigurationElement) menus .get(i); contributeMenu(menuElement, menu, menuAppendIfMissing); } } if (actions != null) { for (int i = 0; i < actions.size(); i++) { ActionDescriptor ad = (ActionDescriptor) actions.get(i); if (menu != null) { contributeMenuAction(ad, menu, menuAppendIfMissing); } if (toolbar != null) { contributeToolbarAction(ad, toolbar, toolAppendIfMissing); } } } } /** * Creates a menu from the information in the menu configuration element and * adds it into the provided menu manager. If 'appendIfMissing' is true, and * menu path slot is not found, it will be created and menu will be added * into it. Otherwise, add operation will fail. */ protected void contributeMenu(IConfigurationElement menuElement, IMenuManager mng, boolean appendIfMissing) { // Get config data. String id = menuElement.getAttribute(IWorkbenchRegistryConstants.ATT_ID); String label = menuElement.getAttribute(IWorkbenchRegistryConstants.ATT_LABEL); String path = menuElement.getAttribute(IWorkbenchRegistryConstants.ATT_PATH); String icon = menuElement.getAttribute(IWorkbenchRegistryConstants.ATT_ICON); ImageDescriptor image = null; if (icon != null) { String extendingPluginId = menuElement.getDeclaringExtension() .getContributor().getName(); image = AbstractUIPlugin.imageDescriptorFromPlugin( extendingPluginId, icon); } if (label == null) { WorkbenchPlugin.log("Plugin \'" //$NON-NLS-1$ + menuElement.getContributor().getName() + "\' invalid Menu Extension (label == null): " + id); //$NON-NLS-1$ return; } // Calculate menu path and group. String group = null; if (path != null) { int loc = path.lastIndexOf('/'); if (loc != -1) { group = path.substring(loc + 1); path = path.substring(0, loc); } else { // assume that path represents a slot // so actual path portion should be null group = path; path = null; } } // Find parent menu. IMenuManager parent = mng; if (path != null) { parent = mng.findMenuUsingPath(path); if (parent == null) { ideLog("Plugin \'" //$NON-NLS-1$ + menuElement.getContributor().getName() + "\' invalid Menu Extension (Path \'" //$NON-NLS-1$ + path + "\' is invalid): " + id); //$NON-NLS-1$ return; } } // Find reference group. if (group == null) { group = IWorkbenchActionConstants.MB_ADDITIONS; } IContributionItem sep = parent.find(group); if (sep == null) { if (appendIfMissing) { addGroup(parent, group); } else { WorkbenchPlugin .log("Plugin \'" //$NON-NLS-1$ + menuElement.getContributor().getName() + "\' invalid Menu Extension (Group \'" //$NON-NLS-1$ + group + "\' is invalid): " + id); //$NON-NLS-1$ return; } } // If the menu does not exist create it. IMenuManager newMenu = parent.findMenuUsingPath(id); if (newMenu == null) { newMenu = new MenuManager(label, image, id); } // Add the menu try { insertAfter(parent, group, newMenu); } catch (IllegalArgumentException e) { WorkbenchPlugin .log("Plugin \'" //$NON-NLS-1$ + menuElement.getContributor().getName() + "\' invalid Menu Extension (Group \'" //$NON-NLS-1$ + group + "\' is missing): " + id); //$NON-NLS-1$ } // Get the menu again as it may be wrapped, otherwise adding // the separators and group markers below will not be wrapped // properly if the menu was just created. newMenu = parent.findMenuUsingPath(id); if (newMenu == null) { WorkbenchPlugin.log("Could not find new menu: " + id); //$NON-NLS-1$ } // Create separators. IConfigurationElement[] children = menuElement.getChildren(); for (IConfigurationElement element : children) { String childName = element.getName(); if (childName.equals(IWorkbenchRegistryConstants.TAG_SEPARATOR)) { contributeSeparator(newMenu, element); } else if (childName.equals(IWorkbenchRegistryConstants.TAG_GROUP_MARKER)) { contributeGroupMarker(newMenu, element); } } } /** * Contributes action from action descriptor into the provided menu manager. */ protected void contributeMenuAction(ActionDescriptor ad, IMenuManager menu, boolean appendIfMissing) { // Get config data. String mpath = ad.getMenuPath(); String mgroup = ad.getMenuGroup(); if (mpath == null && mgroup == null) { return; } // Find parent menu. IMenuManager parent = menu; if (mpath != null) { parent = parent.findMenuUsingPath(mpath); if (parent == null) { ideLog("Plug-in '" + ad.getPluginId() + "' contributed an invalid Menu Extension (Path: '" + mpath + "' is invalid): " + ad.getId()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ return; } } // Find reference group. if (mgroup == null) { mgroup = IWorkbenchActionConstants.MB_ADDITIONS; } IContributionItem sep = parent.find(mgroup); if (sep == null) { if (appendIfMissing) { addGroup(parent, mgroup); } else { WorkbenchPlugin .log("Plug-in '" + ad.getPluginId() + "' contributed an invalid Menu Extension (Group: '" + mgroup + "' is invalid): " + ad.getId()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ return; } } // Add action. try { insertAfter(parent, mgroup, ad); } catch (IllegalArgumentException e) { WorkbenchPlugin .log("Plug-in '" + ad.getPluginId() + "' contributed an invalid Menu Extension (Group: '" + mgroup + "' is missing): " + ad.getId()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } } /** * Creates a named menu separator from the information in the configuration element. * If the separator already exists do not create a second. */ protected void contributeSeparator(IMenuManager menu, IConfigurationElement element) { String id = element.getAttribute(IWorkbenchRegistryConstants.ATT_NAME); if (id == null || id.length() <= 0) { return; } IContributionItem sep = menu.find(id); if (sep != null) { return; } insertMenuGroup(menu, new Separator(id)); } /** * Creates a named menu group marker from the information in the configuration element. * If the marker already exists do not create a second. */ protected void contributeGroupMarker(IMenuManager menu, IConfigurationElement element) { String id = element.getAttribute(IWorkbenchRegistryConstants.ATT_NAME); if (id == null || id.length() <= 0) { return; } IContributionItem marker = menu.find(id); if (marker != null) { return; } insertMenuGroup(menu, new GroupMarker(id)); } /** * Contributes action from the action descriptor into the provided tool bar manager. */ protected void contributeToolbarAction(ActionDescriptor ad, IToolBarManager toolbar, boolean appendIfMissing) { // Get config data. String tId = ad.getToolbarId(); String tgroup = ad.getToolbarGroupId(); if (tId == null && tgroup == null) { return; } // Find reference group. if (tgroup == null) { tgroup = IWorkbenchActionConstants.MB_ADDITIONS; } IContributionItem sep = null; sep = toolbar.find(tgroup); if (sep == null) { if (appendIfMissing) { addGroup(toolbar, tgroup); } else { WorkbenchPlugin .log("Plug-in '" + ad.getPluginId() //$NON-NLS-1$ + "' invalid Toolbar Extension (Group \'" //$NON-NLS-1$ + tgroup + "\' is invalid): " + ad.getId()); //$NON-NLS-1$ return; } } // Add action to tool bar. try { insertAfter(toolbar, tgroup, ad); } catch (IllegalArgumentException e) { WorkbenchPlugin .log("Plug-in '" + ad.getPluginId() //$NON-NLS-1$ + "' invalid Toolbar Extension (Group \'" //$NON-NLS-1$ + tgroup + "\' is missing): " + ad.getId()); //$NON-NLS-1$ } } /** * Inserts the separator or group marker into the menu. Subclasses may override. */ protected void insertMenuGroup(IMenuManager menu, AbstractGroupMarker marker) { menu.add(marker); } /** * Inserts an action after another named contribution item. * Subclasses may override. */ protected void insertAfter(IContributionManager mgr, String refId, ActionDescriptor desc) { final PluginActionContributionItem item = new PluginActionContributionItem(desc.getAction()); item.setMode(desc.getMode()); insertAfter(mgr, refId, item); } /** * Inserts a contribution item after another named contribution item. * Subclasses may override. */ protected void insertAfter(IContributionManager mgr, String refId, IContributionItem item) { mgr.insertAfter(refId, item); } /** * Adds a group to a contribution manager. * Subclasses may override. */ protected void addGroup(IContributionManager mgr, String name) { mgr.add(new Separator(name)); } /** * Disposes this contribution. * * @since 3.1 */ public void dispose() { // do nothing } /** * Disposes the actions. * * @since 3.1 */ protected void disposeActions() { if (actions != null) { for (int i = 0; i < actions.size(); i++) { PluginAction proxy = ((ActionDescriptor) actions.get(i)) .getAction(); proxy.dispose(); } actions = null; } } } private static boolean allowIdeLogging = false; /** * If set to <code>false</code>, some of the logs that can be caused by * use IDE plugins from an RCP app will be ignored. * * @param b * Log the errors or not. * @since 3.3 */ public static void setAllowIdeLogging(boolean b) { allowIdeLogging = b; } /** * These are log messages that should be ignored by RCP apps when using the * IDE plugins. * * @param msg * @since 3.3 */ private static void ideLog(String msg) { if (allowIdeLogging) { WorkbenchPlugin.log(msg); } } }